using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using XTYAPI.PLC;


namespace XTYAPI.通讯
{
    public class SiemensS7
    {

        //TcpClient对象
        private Socket tcpClient;
        public Socket Tcpclient { get { return tcpClient; } }
        /// MaxPDUSize
        public short MaxPDUSize { get; set; } = 240;
        public int ConnectTimeOut { get; set; } = 2000;

        // 一次正常的交互的互斥锁
        private SimpleHybirdLock InteractiveLock = new SimpleHybirdLock();

        /// <summary>
        /// 接受返回报文等待次数，每次为10ms
        /// </summary>
        public int WaitTimes { get; set; } = 5;

        private byte[] S7_STOP = {
            0x03, 0x00, 0x00, 0x21, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00,
            0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41,
            0x4d
        };

        private byte[] S7_HOT_START = {
            0x03, 0x00, 0x00, 0x25, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x14, 0x00,
            0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x09, 0x50, 0x5f, 0x50, 0x52,
            0x4f, 0x47, 0x52, 0x41, 0x4d
        };

        private byte[] S7_COLD_START = {
            0x03, 0x00, 0x00, 0x27, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x16, 0x00,
            0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x02, 0x43, 0x20, 0x09, 0x50, 0x5f,
            0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d
        };

        const byte pduStart = 0x28;            // CPU start
        const byte pduStop = 0x29;             // CPU stop
        const byte pduAlreadyStarted = 0x02;   // CPU already in run mode
        const byte pduAlreadyStopped = 0x07;   // CPU already in stop mode

        #region PLC连接
        /// <summary>
        /// 建立与PLC的连接
        /// </summary>
        /// <param name="Ip">IP地址</param>
        /// <param name="Cpu">CPU类型</param>
        /// <param name="Rack">机架号</param>
        /// <param name="Slot">插槽号</param>
        /// <returns></returns>
        public bool Connect(string Ip, CPU_Type Cpu, int Rack, int Slot)
        {
     
            this.tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建socket tcp连接对象
            this.tcpClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000);//接收超时2s
            this.tcpClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 2000);//发送超时2s
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(Ip), 102);//s7协议对应的端口号是102
            IAsyncResult connResult = this.tcpClient.BeginConnect(ipe, null, null);//开始异步连接
            connResult.AsyncWaitHandle.WaitOne(2000, true); //阻塞线程获取连接结果

            if (!connResult.IsCompleted)
            {
                //连接失败关闭连接并返回失败
                tcpClient.Close();
                return false;
            }

            //2、第一次握手
            var response = SendAndReceive(GetCOTPConnectionRequest(Cpu, Rack, Slot));
            if (response == null)
            {
                return false;
            }

            //3、第二次握手
            var s7data = SendAndReceive(GetS7ConnectionSetup(Cpu));
            if (s7data == null)
            {
                return false;
            }

            if (s7data.Length == 27)
            {
                MaxPDUSize = (short)(s7data[25] * 256 + s7data[26]);
            }
            return true;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnect()
        {
            if (tcpClient != null)
            {
                if (tcpClient.Connected) tcpClient.Close();
            }
        }

        #region 获取COTP报文
        /// <summary>
        /// 获取COTP报文
        /// </summary>
        /// <param name="Cpu"></param>
        /// <param name="Rack"></param>
        /// <param name="Slot"></param>
        /// <returns></returns>
        private byte[] GetCOTPConnectionRequest(CPU_Type Cpu, int Rack, int Slot)
        {
            byte[] array = new byte[]
             {
                0x03,  0x00, 0x00, 0x16,  // TPKT
                0x11,  // COTP Header Length
                0xE0,  // Connect Request 
                0x00,   0x00,  // Destination Reference
                0x00,  0x01,  // Source Reference
                0x00,  // Flags
                0xC0,  // Parameter Code (tpdu-size)
                0x01,  // Parameter Length
                0x0A,  // TPDU Size (2^10 = 1024)
                0xC1,  // Parameter Code (src-tasp)
                0x02,  // Parameter Length
                0x01,  0x02,  // Source TASP
                0xC2,  // Parameter Code (dst-tasp)
                0x02,  // Parameter Length
                0x01,   0x00    //Destination TASP
             };
            switch (Cpu)
            {
                case CPU_Type.S7200:
                    break;
                case CPU_Type.S7200SMART:
                    array[9] = 0x01;
                    array[10] = 0x00;
                    array[11] = 0xC1;   //Parameter Code 0xC1
                    array[12] = 0x02;   //Parameter Length 0x02
                    array[13] = 0x10;   //Source TSAP 0x0101
                    array[14] = 0x00;   //Source TSAP 0x0101
                    array[15] = 0xC2;    //dst-tasp 0xC2
                    array[16] = 0x02;    //Parameter Length 0x02
                    array[17] = 0x03;    //Destination TSAP 0x0101
                    array[18] = 0x00;    //Destination TSAP 0x0101  
                    array[19] = 0x00;    //Parameter Code tpdu-Size 0xC0
                    array[20] = 0x01;    //Parameter Length 0x01
                    array[21] = 0x0A;    //TPDU Size
                    break;
                case CPU_Type.S7300:
                    array[21] = 0x02;
                    break;
                case CPU_Type.S7400:
                    array[17] = 0x00;
                    array[21] = (byte)((Rack * 0x20) + Slot);
                    break;
                case CPU_Type.S71200:
                    array[21] = 0;
                    break;
                case CPU_Type.S71500:
                    array[21] = 0;
                    break;
                default:
                    break;
            }
            return array;
        }
        #endregion

        #region 获取S7ConnectionSetup报文

        /// <summary>
        /// 获取S7ConnectionSetup报文
        /// </summary>
        /// <param name="Cpu"></param>
        /// <returns></returns>
        public byte[] GetS7ConnectionSetup(CPU_Type Cpu)
        {
            byte[] array = new byte[] {
                3, 0, 0, 25, 2, 240, 128, 50, 1, 0, 0, 255, 255, 0, 8, 0, 0, 240, 0, 0, 3, 0, 3,7, 128 //Try 1920 PDU Size. Same as libnodave.
            };
            switch (Cpu)
            {
                case CPU_Type.S7200SMART:
                    array[11] = 0xCC;   //Protocol Data Unit Reference:52417
                    array[12] = 0xC1;
                    array[13] = 0x00;  //Parameter Length 0x08
                    array[14] = 0x08;   //Parameter Length 0x08
                    array[15] = 0x00;   //Data Length 0x0000
                    array[16] = 0x00;
                    array[17] = 0xF0;   // Function 0xF0
                    array[18] = 0x00;   //备用
                    array[19] = 0x00;   //Max AmQ 0x0001
                    array[20] = 0x01;
                    array[21] = 0x00;   //Max AmQ 0x0001
                    array[22] = 0x01;
                    array[23] = 0x03;   //PDU Length 960 
                    array[24] = 0xC0;
                    break;
                default:
                    break;
            }
            return array;
        }

        #endregion

        #endregion

        #region 读取指定存储区的指定长度字节
        
        public byte[] ReadBytes(StoreType StoreType, int DB, int StartByteAdr, int count)
        {
            InteractiveLock.Enter();
            try
            {
                List<byte> resultBytes = new List<byte>();
                int index = StartByteAdr;
                while (count > 0)
                {
                    var maxToRead = (int)Math.Min(count, MaxPDUSize - 18);
                    byte[] bytes = ReadBytesWithSingleRequest(StoreType, DB, index, maxToRead);
                    if (bytes == null)
                        return resultBytes.ToArray();
                    resultBytes.AddRange(bytes);
                    count -= maxToRead;
                    index += maxToRead;
                }
                return resultBytes.Count > 0 ? resultBytes.ToArray() : null;
            }
            catch (Exception)
            {
                return null;
            }
            finally
            {
                InteractiveLock.Leave();
            }
        }

        /// <summary>
        /// 读取指定存储区的指定长度字节
        /// </summary>
        /// <param name="StoreType"></param>
        /// <param name="DB"></param>
        /// <param name="StartByteAdr"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        private byte[] ReadBytesWithSingleRequest(StoreType StoreType, int DB, int StartByteAdr, int count)
        {
            //DB号仅在DB存储区时有效
            DB = StoreType == StoreType.DataBlock ? DB : 0;

            byte[] array = new byte[count];
            byte[] result;
            try
            {
                int num = 31;
                ByteArray byteArray = new ByteArray();
                ByteArray byteArray1 = byteArray;
                byte[] array2 = new byte[3];
                array2[0] = 3;
                byteArray1.Add(array2);
                byteArray.Add((byte)num);
                byteArray.Add(new byte[]
                {
                    2,
                    240,
                    128,
                    50,
                    1,
                    0,
                    0,
                    0,
                    0,
                    0,
                    14,
                    0,
                    0,
                    4,
                    1,
                    18,
                    10,
                    16
                });
                switch (StoreType)
                {
                    case StoreType.Counter:
                    case StoreType.Timer:
                        byteArray.Add((byte)StoreType);
                        break;
                    default:
                        byteArray.Add(2);
                        break;
                }
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)count));
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)DB));
                byteArray.Add((byte)StoreType);
                byteArray.Add(0);
                switch (StoreType)
                {
                    case StoreType.Counter:
                    case StoreType.Timer:
                        byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)StartByteAdr));
                        break;
                    default:
                        byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(StartByteAdr * 8)));
                        break;
                }
                byte[] array3 = SendAndReceiveWithoutLock(byteArray.array);

                if (array3 == null)
                {
                    throw new Exception();
                }
                if (array3[21] != 255)
                {
                    throw new Exception();
                }
                for (int i = 0; i < count; i++)
                {
                    array[i] = array3[i + 25];
                }
                result = array;
            }
            catch
            {
                result = null;
            }
            return result;
        }
        #endregion

        #region 读取指定的变量值
        public object Read(StoreType StoreType, int DB, int StartByteAdr, VarType VarType, int VarCount)
        {
            object result;
            switch (VarType)
            {
                case VarType.Byte:
                    {
                        int num = VarCount;
                        if (num < 1)
                        {
                            num = 1;
                        }
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = array[0];
                            }
                            else
                            {
                                result = array;
                            }
                        }
                        break;
                    }
                case VarType.Word:
                    {
                        int num = VarCount * 2;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = UShortLib.GetUShortFromByteArray(array);
                            }
                            else
                            {
                                result = UShortLib.GetUShortArrayFromByteArray(array);
                            }
                        }
                        break;
                    }
                case VarType.DWord:
                    {
                        int num = VarCount * 4;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = UIntLib.GetUIntFromByteArray(array);
                            }
                            else
                            {
                                result = UIntLib.GetUIntArrayFromByteArray(array);
                            }
                        }
                        break;
                    }
                case VarType.Int:
                    {
                        int num = VarCount * 2;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = IntLib.GetIntFromByteArray(array);
                            }
                            else
                            {
                                result = IntLib.GetIntArrayFromByteArray(array);
                            }
                        }
                        break;
                    }
                case VarType.DInt:
                    {
                        int num = VarCount * 4;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = IntLib.GetIntFromByteArray(array);
                            }
                            else
                            {
                                result = IntLib.GetIntArrayFromByteArray(array);
                            }
                        }
                        break;
                    }
                case VarType.Real:
                    {
                        int num = VarCount * 4;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = FloatLib.GetFloatFromByteArray(array);
                            }
                            else
                            {
                                result = FloatLib.GetFloatArrayFromByteArray(array);
                            }
                        }
                        break;
                    }
                case VarType.String:
                    {
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, VarCount);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            result = Encoding.ASCII.GetString(array);
                        }
                        break;
                    }
                case VarType.Timer:
                    {
                        int num = VarCount * 2;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = Timers.FromByteArray(array);
                            }
                            else
                            {
                                result = Timers.ToArray(array);
                            }
                        }
                        break;
                    }
                case VarType.Counter:
                    {
                        int num = VarCount * 2;
                        byte[] array = this.ReadBytes(StoreType, DB, StartByteAdr, num);
                        if (array == null)
                        {
                            result = null;
                        }
                        else
                        {
                            if (VarCount == 1)
                            {
                                result = Counter.FromByteArray(array);
                            }
                            else
                            {
                                result = Counter.ToArray(array);
                            }
                        }
                        break;
                    }
                default:
                    result = null;
                    break;
            }
            return result;
        }
        #endregion

        #region 根据变量地址读取数值

        public object Read(string variable, VarType VarType)
        {
            string[] array = new string[8];
            string text = variable.ToUpper();
            text = text.Replace(" ", "");
            object result;
            try
            {
                string text2 = text.Substring(0, 2);
                switch (text2)
                {
                    case "DB":
                        {
                            string[] array2 = text.Split(new char[]
                            {
                                '.'
                            });
                            if (array2.Length < 2)
                            {
                                throw new Exception();
                            }
                            int dB = int.Parse(array2[0].Substring(2));
                            string text3 = array2[1].Substring(0, 3);
                            int num2 = int.Parse(array2[1].Substring(3));
                            text2 = text3;
                            if (text2 != null)
                            {
                                if (text2 == "DBB")
                                {
                                    byte b = (byte)this.Read(StoreType.DataBlock, dB, num2, VarType.Byte, 1);
                                    result = b;
                                    return result;
                                }
                                if (text2 == "DBW")
                                {
                                    switch (VarType)
                                    {
                                        case VarType.Int:
                                            return (short)this.Read(StoreType.DataBlock, dB, num2, VarType.Int, 1);
                                        default:
                                            return (ushort)this.Read(StoreType.DataBlock, dB, num2, VarType.Word, 1);
                                    }
                                }
                                if (text2 == "DBD")
                                {
                                    switch (VarType)
                                    {
                                        case VarType.Real:
                                            return Convert.ToSingle(this.Read(StoreType.DataBlock, dB, num2, VarType.Real, 1));
                                        case VarType.DInt:
                                            return Convert.ToInt32(this.Read(StoreType.DataBlock, dB, num2, VarType.DInt, 1));
                                        default:
                                            return Convert.ToUInt32(this.Read(StoreType.DataBlock, dB, num2, VarType.DWord, 1));
                                    }
                                }
                                if (text2 == "DBX")
                                {
                                    int startByteAdr = num2;
                                    int num4 = int.Parse(array2[2]);
                                    if (num4 > 7)
                                    {
                                        throw new Exception();
                                    }
                                    int num = Convert.ToInt32(Convert.ToString(Convert.ToInt32(this.Read(StoreType.DataBlock, dB, startByteAdr, VarType.Byte, 1)), 2));
                                    string text4 = num.ToString("0#######");
                                    array[num4] = text4.Substring(7 - num4, 1);
                                    result = array[num4];
                                    return result;
                                }
                            }
                            throw new Exception();
                        }
                    case "IB":
                        {
                            byte b2 = (byte)this.Read(StoreType.Input, 0, int.Parse(text.Substring(2)), VarType.Byte, 1);
                            result = b2;
                            return result;
                        }
                    case "IW":
                        {
                            switch (VarType)
                            {
                                case VarType.Int:
                                    return (short)this.Read(StoreType.Input, 0, int.Parse(text.Substring(2)), VarType.Int, 1);
                                default:
                                    return (ushort)this.Read(StoreType.Input, 0, int.Parse(text.Substring(2)), VarType.Word, 1);
                            }
                        }
                    case "ID":
                        {
                            switch (VarType)
                            {
                                case VarType.Real:
                                    return Convert.ToSingle(this.Read(StoreType.Input, 0, int.Parse(text.Substring(2)), VarType.Real, 1));
                                case VarType.DInt:
                                    return Convert.ToInt32(this.Read(StoreType.Input, 0, int.Parse(text.Substring(2)), VarType.DInt, 1));
                                default:
                                    return Convert.ToUInt32(this.Read(StoreType.Input, 0, int.Parse(text.Substring(2)), VarType.DWord, 1));
                            }
                        }
                    case "QB":
                        {
                            byte b2 = (byte)this.Read(StoreType.Output, 0, int.Parse(text.Substring(2)), VarType.Byte, 1);
                            result = b2;
                            return result;
                        }
                    case "QW":
                        {
                            switch (VarType)
                            {
                                case VarType.Int:
                                    return (short)this.Read(StoreType.Output, 0, int.Parse(text.Substring(2)), VarType.Int, 1);
                                default:
                                    return (ushort)this.Read(StoreType.Output, 0, int.Parse(text.Substring(2)), VarType.Word, 1);
                            }
                        }
                    case "QD":
                        {
                            switch (VarType)
                            {
                                case VarType.Real:
                                    return Convert.ToSingle(this.Read(StoreType.Output, 0, int.Parse(text.Substring(2)), VarType.Real, 1));
                                case VarType.DInt:
                                    return Convert.ToInt32(this.Read(StoreType.Output, 0, int.Parse(text.Substring(2)), VarType.DInt, 1));
                                default:
                                    return Convert.ToUInt32(this.Read(StoreType.Output, 0, int.Parse(text.Substring(2)), VarType.DWord, 1));
                            }
                        }
                    case "MB":
                        {
                            byte b2 = (byte)this.Read(StoreType.Marker, 0, int.Parse(text.Substring(2)), VarType.Byte, 1);
                            result = b2;
                            return result;
                        }
                    case "MW":
                        {
                            switch (VarType)
                            {
                                case VarType.Int:
                                    return (short)this.Read(StoreType.Marker, 0, int.Parse(text.Substring(2)), VarType.Int, 1);
                                default:
                                    return (ushort)this.Read(StoreType.Marker, 0, int.Parse(text.Substring(2)), VarType.Word, 1);
                            }
                        }
                    case "MD":
                        {
                            switch (VarType)
                            {
                                case VarType.Real:
                                    return Convert.ToSingle(this.Read(StoreType.Marker, 0, int.Parse(text.Substring(2)), VarType.Real, 1));
                                case VarType.DInt:
                                    return Convert.ToInt32(this.Read(StoreType.Marker, 0, int.Parse(text.Substring(2)), VarType.DInt, 1));
                                default:
                                    return Convert.ToUInt32(this.Read(StoreType.Marker, 0, int.Parse(text.Substring(2)), VarType.DWord, 1));
                            }
                        }
                }
                text2 = text.Substring(0, 1);
                if (text2 != null)
                {
                    Dictionary<string, int> LIST = new Dictionary<string, int>(8)
                        {
                        {"E",0},{"I",1},{"A",2},{"Q",3},{"M",4},{"T",5},{"Z",6},{"C",7}
                        };

                    int num;
                    if (LIST.TryGetValue(text2, out num))
                    {
                        StoreType dataType;
                        switch (num)
                        {
                            case 0:
                            case 1:
                                dataType = StoreType.Input;
                                break;
                            case 2:
                            case 3:
                                dataType = StoreType.Output;
                                break;
                            case 4:
                                dataType = StoreType.Marker;
                                break;
                            case 5:
                                {
                                    return (ushort)this.Read(StoreType.Timer, 0, int.Parse(text.Substring(1)), VarType.Timer, 1);

                                }
                            case 6:
                            case 7:
                                {
                                    return (ushort)this.Read(StoreType.Counter, 0, int.Parse(text.Substring(1)), VarType.Counter, 1);

                                }
                            default:
                                goto IL_59A;
                        }
                        string text5 = text.Substring(1);
                        if (text5.IndexOf(".") == -1)
                        {
                            throw new Exception();
                        }
                        int startByteAdr = int.Parse(text5.Substring(0, text5.IndexOf(".")));
                        int num4 = int.Parse(text5.Substring(text5.IndexOf(".") + 1));
                        if (num4 > 7)
                        {
                            throw new Exception();
                        }
                        num = Convert.ToInt32(Convert.ToString(Convert.ToInt32(this.Read(dataType, 0, startByteAdr, VarType.Byte, 1)), 2));
                        string text6 = num.ToString("0#######");
                        array[num4] = text6.Substring(7 - num4, 1);
                        result = array[num4];
                        return result;
                    }
                }
            IL_59A:
                result = null;
            }
            catch
            {
                result = null;
            }
            return result;
        }
        #endregion

        #region 根据数据类型、DB号、起始地址写入数值

        public bool WriteBytes(StoreType StoreType, int DB, int StartByteAdr, byte[] value)
        {
            int localIndex = 0;
            int count = value.Length;
            while (count > 0)
            {
                var maxToWrite = Math.Min(count, 200);
                if (WriteBytesSingleRequest(StoreType, DB, StartByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray()))
                {
                    count -= maxToWrite;
                    localIndex += maxToWrite;
                }
                else
                {
                    return false;
                }
            }
            return true;
        }

        public bool WriteBytesSingleRequest(StoreType StoreType, int DB, int StartByteAdr, byte[] value)
        {
            byte[] array = new byte[512];
            bool result=true;
            try
            {
                int num = value.Length;
                int num2 = 35 + value.Length;
                ByteArray byteArray = new ByteArray();
                ByteArray arg_33_0 = byteArray;
                byte[] array2 = new byte[3];
                array2[0] = 3;
                arg_33_0.Add(array2);
                byteArray.Add((byte)num2);
                byteArray.Add(new byte[]
                {
                    2,
                    240,
                    128,
                    50,
                    1,
                    0,
                    0
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(num - 1)));
                byteArray.Add(new byte[]
                {
                    0,
                    14
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(num + 4)));
                byteArray.Add(new byte[]
                {
                    5,
                    1,
                    18,
                    10,
                    16,
                    2
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)num));
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)DB));
                byteArray.Add((byte)StoreType);
                byteArray.Add(0);
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(StartByteAdr * 8)));
                byteArray.Add(new byte[]
                {
                    0,
                    4
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(num * 8)));
                byteArray.Add(value);
                array = SendAndReceive(byteArray.array);
                if (array == null)
                {
                    result = false;
                }
                if (array?[21] != 255)
                {
                    result = false;
                }
                //result = true;
            }
            catch (Exception)
            {
                result = false;
            }
            return result;
        }


        public bool WriteBit(StoreType dataType, int db, int startByteAdr, int bitAdr, bool value)
        {
            if (bitAdr < 0 || bitAdr > 7)
                return false;

            return WriteBitSingleRequest(dataType, db, startByteAdr, bitAdr, value);
        }

        public bool WriteBitSingleRequest(StoreType StoreType, int DB, int StartByteAdr, int bitAddress, bool bitValue)
        {
            byte[] array = new byte[512];
            bool result;
            try
            {
                var value = new[] { bitValue ? (byte)1 : (byte)0 };
                int num = value.Length;
                int num2 = 35 + value.Length;
                ByteArray byteArray = new ByteArray();
                ByteArray arg_33_0 = byteArray;
                byte[] array2 = new byte[3];
                array2[0] = 3;
                arg_33_0.Add(array2);
                byteArray.Add((byte)num2);
                byteArray.Add(new byte[]
                {
                    2,
                    240,
                    128,
                    50,
                    1,
                    0,
                    0
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(num - 1)));
                byteArray.Add(new byte[]
                {
                    0,
                    14
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(num + 4)));
                byteArray.Add(new byte[]
                {
                    5,
                    1,
                    18,
                    10,
                    16,
                    1
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)num));
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)DB));
                byteArray.Add((byte)StoreType);
                byteArray.Add(0);
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(StartByteAdr * 8 + bitAddress)));
                byteArray.Add(new byte[]
                {
                    0,
                    3
                });
                byteArray.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(num)));
                byteArray.Add(value);
                array = SendAndReceive(byteArray.array);
                if (array == null)
                {
                    throw new Exception();
                }
                if (array[21] != 255)
                {
                    throw new Exception();
                }
                result = true;
            }
            catch (Exception)
            {
                result = false;
            }
            return result;
        }
        #endregion

        #region 写入单个变量数值
        public bool Write(StoreType StoreType, int DB, int StartByteAdr, object value, int bitAdr = -1)
        {
            if (bitAdr == -1)
            {
                string name2 = value.GetType().Name;
                bool result;
                if (name2 != null)
                {
                    Dictionary<string, int> list = new Dictionary<string, int>(13)
                    {

                        {
                            "Byte",
                            0
                        },

                        {
                            "Int16",
                            1
                        },

                        {
                            "UInt16",
                            2
                        },

                        {
                            "Int32",
                            3
                        },

                        {
                            "UInt32",
                            4
                        },

                        {
                            "Single",
                            5
                        },

                        {
                            "Byte[]",
                            6
                        },

                        {
                            "Int16[]",
                            7
                        },

                        {
                            "UInt16[]",
                            8
                        },

                        {
                            "Int32[]",
                            9
                        },

                        {
                            "UInt32[]",
                            10
                        },

                        {
                            "Single[]",
                            11
                        },

                        {
                            "String",
                            12
                        },
                        {
                            "Int64",
                            13
                        },

                        {
                            "UInt64",
                            14
                        },

                        {
                            "Double",
                            15
                        },

                    };

                    int num;
                    if (list.TryGetValue(name2, out num))
                    {
                        byte[] value2;
                        switch (num)
                        {
                            case 0:
                                value2 = ByteArrayLib.GetByteArrayFromByte((byte)value);
                                break;
                            case 1:
                                value2 = ByteArrayLib.GetByteArrayFromShort((short)value);
                                break;
                            case 2:
                                value2 = ByteArrayLib.GetByteArrayFromUShort((ushort)value);
                                break;
                            case 3:
                                value2 = ByteArrayLib.GetByteArrayFromInt((int)value);
                                break;
                            case 4:
                                value2 = ByteArrayLib.GetByteArrayFromUInt((uint)value);
                                break;
                            case 5:
                                value2 = ByteArrayLib.GetByteArrayFromFloat((float)value);
                                break;
                            case 6:
                                value2 = (byte[])value;
                                break;
                            case 7:
                                value2 = ByteArrayLib.GetByteArrayFromShortArray((short[])value);
                                break;
                            case 8:
                                value2 = ByteArrayLib.GetByteArrayFromUShortArray((ushort[])value);
                                break;
                            case 9:
                                value2 = ByteArrayLib.GetByteArrayFromIntArray((int[])value);
                                break;
                            case 10:
                                value2 = ByteArrayLib.GetByteArrayFromUIntArray((uint[])value);
                                break;
                            case 11:
                                value2 = ByteArrayLib.GetByteArrayFromFloatArray((float[])value);
                                break;
                            case 12:
                                value2 = ByteArrayLib.GetByteArrayFromString((string)value, Encoding.GetEncoding("GBK"));
                                break;
                            case 13:
                                value2 = ByteArrayLib.GetByteArrayFromLong((long)value);
                                break;
                            case 14:
                                value2 = ByteArrayLib.GetByteArrayFromULong((ulong)value);
                                break;
                            case 15:
                                value2 = ByteArrayLib.GetByteArrayFromDouble((double)value);
                                break;
                            default:
                                goto IL_1FB;
                        }
                        result = this.WriteBytes(StoreType, DB, StartByteAdr, value2);
                        return result;
                    }
                }
            IL_1FB:
                result = false;
                return result;
            }
            else
            {
                if (value is bool)
                {
                    return WriteBit(StoreType, DB, StartByteAdr, bitAdr, (bool)value);
                }
                else if (value is int intValue)
                {
                    if (bitAdr < 0 || bitAdr > 7)
                        return false;
                    return WriteBit(StoreType, DB, StartByteAdr, bitAdr, intValue == 1);
                }
                else
                {
                    return false;
                }
            }
        }
        #endregion

        #region 根据变量地址写入数值
        public bool Write(string variable, object value)
        {
            string text = variable.ToUpper();
            text = text.Replace(" ", "");
            bool result;
            try
            {
                string text2 = text.Substring(0, 2);
                switch (text2)
                {
                    case "DB":
                        {
                            string[] array = text.Split(new char[]
                             {
                               '.'
                             });
                            if (array.Length < 2)
                            {
                                throw new Exception();
                            }
                            int dB = int.Parse(array[0].Substring(2));
                            string text3 = array[1].Substring(0, 3);
                            int num2 = 0;
                            string text4 = array[1].Substring(3);

                            if (!text4.Contains('|'))
                            {
                                num2 = int.Parse(text4);
                            }
                            else
                            {
                                string[] temp = text4.Split(new char[]
                                 {
                               '|'
                                 });
                                num2 = int.Parse(temp[0]);
                            }

                            text2 = text3;
                            if (text2 != null)
                            {
                                if (text2 == "DBB")
                                {
                                    object obj = Convert.ChangeType(value, typeof(byte));
                                    result = this.Write(StoreType.DataBlock, dB, num2, (byte)obj);
                                    return result;
                                }
                                if (text2 == "DBW")
                                {
                                    if (value is Int16)
                                    {
                                        object obj = Convert.ChangeType(value, typeof(Int16));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (Int16)obj);
                                    }
                                    else
                                    {
                                        object obj = Convert.ChangeType(value, typeof(UInt16));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (UInt16)obj);
                                    }
                                    return result;
                                }
                                if (text2 == "DBD")
                                {
                                    if (value is Int32)
                                    {
                                        object obj = Convert.ChangeType(value, typeof(Int32));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (Int32)obj);
                                    }
                                    else if (value is UInt32)
                                    {
                                        object obj = Convert.ChangeType(value, typeof(UInt32));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (UInt32)obj);
                                    }
                                    else
                                    {
                                        object obj = Convert.ChangeType(value, typeof(float));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (float)obj);
                                    }
                                    return result;
                                }
                                if (text2 == "DBR")
                                {
                                    if (value is Int64)
                                    {
                                        object obj = Convert.ChangeType(value, typeof(Int64));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (Int64)obj);
                                    }
                                    else if (value is UInt64)
                                    {
                                        object obj = Convert.ChangeType(value, typeof(UInt64));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (UInt64)obj);
                                    }
                                    else
                                    {
                                        object obj = Convert.ChangeType(value, typeof(double));
                                        result = this.Write(StoreType.DataBlock, dB, num2, (double)obj);
                                    }
                                    return result;
                                }
                                if (text2 == "DBS")
                                {
                                    //表示为String类型
                                    if (!text4.Contains('|'))
                                    {
                                        byte[] set = ByteArrayLib.GetByteArrayFromSiemensString((string)value);
                                        set[0] = byte.Parse(array[2]);
                                        result = this.Write(StoreType.DataBlock, dB, num2, set);
                                        return result;
                                    }
                                    //表示为Char数组
                                    else
                                    {
                                        byte[] set = ByteArrayLib.GetByteArrayFromString((string)value, Encoding.GetEncoding("GBK"));
                                        result = this.Write(StoreType.DataBlock, dB, num2, set);
                                        return result;
                                    }
                                }
                                if (text2 == "DBX")
                                {
                                    int startByteAdr = num2;
                                    int num3 = int.Parse(array[2]);
                                    result = this.Write(StoreType.DataBlock, dB, startByteAdr, value, num3);
                                    return result;
                                }
                            }
                            return false;
                        }

                    case "IB":
                        {
                            object obj = Convert.ChangeType(value, typeof(byte));
                            result = this.Write(StoreType.Input, 0, int.Parse(text.Substring(2)), (byte)obj);
                            return result;
                        }
                    case "IW":
                        {
                            object obj = Convert.ChangeType(value, typeof(ushort));
                            result = this.Write(StoreType.Input, 0, int.Parse(text.Substring(2)), (ushort)obj);
                            return result;
                        }
                    case "ID":
                        {
                            object obj = Convert.ChangeType(value, typeof(uint));
                            result = this.Write(StoreType.Input, 0, int.Parse(text.Substring(2)), (uint)obj);
                            return result;
                        }
                    case "QB":
                        {
                            object obj = Convert.ChangeType(value, typeof(byte));
                            result = this.Write(StoreType.Output, 0, int.Parse(text.Substring(2)), (byte)obj);
                            return result;
                        }
                    case "QW":
                        {
                            object obj = Convert.ChangeType(value, typeof(ushort));
                            result = this.Write(StoreType.Output, 0, int.Parse(text.Substring(2)), (ushort)obj);
                            return result;
                        }
                    case "QD":
                        {
                            object obj = Convert.ChangeType(value, typeof(uint));
                            result = this.Write(StoreType.Output, 0, int.Parse(text.Substring(2)), (uint)obj);
                            return result;
                        }
                    case "MB":
                        {
                            object obj = Convert.ChangeType(value, typeof(byte));
                            result = this.Write(StoreType.Marker, 0, int.Parse(text.Substring(2)), (byte)obj);
                            return result;
                        }
                    case "MW":
                        {

                            if (value is Int16)
                            {
                                object obj = Convert.ChangeType(value, typeof(Int16));
                                result = this.Write(StoreType.Marker, 0, int.Parse(text.Substring(2)), (short)obj);
                            }
                            else
                            {
                                object obj = Convert.ChangeType(value, typeof(UInt16));
                                result = this.Write(StoreType.Marker, 0, int.Parse(text.Substring(2)), (ushort)obj);
                            }

                            return result;
                        }
                    case "MD":
                        if (value is Int32)
                        {
                            object obj = Convert.ChangeType(value, typeof(Int32));
                            result = this.Write(StoreType.Marker, 0, int.Parse(text.Substring(2)), (Int32)obj);
                        }
                        else if (value is UInt32)
                        {
                            object obj = Convert.ChangeType(value, typeof(UInt32));
                            result = this.Write(StoreType.Marker, 0, int.Parse(text.Substring(2)), (UInt32)obj);
                        }
                        else
                        {
                            object obj = Convert.ChangeType(value, typeof(float));
                            result = this.Write(StoreType.Marker, 0, int.Parse(text.Substring(2)), (float)obj);
                        }

                        return result;

                        //result = this.Write(DataType.Marker, 0, int.Parse(text.Substring(2)), value);
                        //return result;
                }
                text2 = text.Substring(0, 1);
                if (text2 != null)
                {
                    Dictionary<string, int> list = new Dictionary<string, int>(8)
                        {

                            {
                                "E",
                                0
                            },

                            {
                                "I",
                                1
                            },

                            {
                                "A",
                                2
                            },

                            {
                                "Q",
                                3
                            },

                            {
                                "M",
                                4
                            },

                            {
                                "T",
                                5
                            },

                            {
                                "Z",
                                6
                            },

                            {
                                "C",
                                7
                            }
                        };

                    int num;
                    if (list.TryGetValue(text2, out num))
                    {
                        StoreType dataType;
                        switch (num)
                        {
                            case 0:
                            case 1:
                                dataType = StoreType.Input;
                                break;
                            case 2:
                            case 3:
                                dataType = StoreType.Output;
                                break;
                            case 4:
                                dataType = StoreType.Marker;
                                break;
                            case 5:
                                result = this.Write(StoreType.Timer, 0, int.Parse(text.Substring(1)), (double)value);
                                return result;
                            case 6:
                            case 7:
                                result = this.Write(StoreType.Counter, 0, int.Parse(text.Substring(1)), (short)value);
                                return result;
                            default:
                                goto IL_671;
                        }
                        string text4 = text.Substring(1);
                        if (text4.IndexOf(".") == -1)
                        {
                            throw new Exception();
                        }
                        int startByteAdr = int.Parse(text4.Substring(0, text4.IndexOf(".")));
                        int num3 = int.Parse(text4.Substring(text4.IndexOf(".") + 1));

                        result = this.Write(dataType, 0, startByteAdr, value, num3);
                        return result;
                    }
                }
            IL_671:
                throw new Exception();
            }
            catch
            {
                result = false;
            }
            return result;
        }
        #endregion

        #region 冷启动

        public bool CoolStart()
        {
            byte[] result = SendAndReceive(this.S7_COLD_START);
            if (result != null)
            {
                if (result.Length >= 20)
                {
                    if (result[19] == pduStart)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else if (result.Length >= 21)
                {
                    if (result[20] == pduAlreadyStarted)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }

            }
            else
            {
                return false;
            }
        }

        #endregion

        #region 暖启动

        public bool WarmStart()
        {
            byte[] result = SendAndReceive(this.S7_HOT_START);
            if (result != null)
            {
                if (result.Length >= 20)
                {
                    if (result[19] == pduStart)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else if (result.Length >= 21)
                {
                    if (result[20] == pduAlreadyStarted)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }

            }
            else
            {
                return false;
            }
        }

        #endregion

        #region 停止

        public bool StopCPU()
        {
            byte[] result = SendAndReceive(this.S7_STOP);
            if (result != null)
            {
                if (result.Length == 19)
                {
                    return true;
                }
                if (result.Length >= 20)
                {
                    if (result[19] == pduStop)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else if (result.Length >= 21)
                {
                    if (result[20] == pduAlreadyStopped)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }

                else
                {
                    return false;
                }

            }
            else
            {
                return false;
            }
        }

        #endregion

        #region 发送报文并接受
        /// <summary>
        /// 发送报文并接受
        /// </summary>
        /// <param name="SendByte"></param>
        /// <returns></returns>
        private byte[] SendAndReceive(byte[] SendByte)
        {
            InteractiveLock.Enter();
            try
            {
                tcpClient.Send(SendByte, SendByte.Length, SocketFlags.None);
                int k = tcpClient.Available;
                int timer = 0;
                while (k == 0)
                {
                    timer++;
                    k = tcpClient.Available;
                    Thread.Sleep(10);
                    if (timer > WaitTimes)
                        break;
                }
                byte[] myBufferBytes = new byte[k];
                tcpClient.Receive(myBufferBytes, k, SocketFlags.None);
                return myBufferBytes;
            }
            catch (Exception)
            {
                return null;
            }
            finally
            {
                InteractiveLock.Leave();
            }
        }

        /// <summary>
        /// 发送报文并接受
        /// </summary>
        /// <param name="SendByte"></param>
        /// <returns></returns>
        private byte[] SendAndReceiveWithoutLock(byte[] SendByte)
        {
            try
            {
                tcpClient.Send(SendByte, SendByte.Length, SocketFlags.None);
                int k = tcpClient.Available;
                int timer = 0;
                while (k == 0)
                {
                    timer++;
                    k = tcpClient.Available;
                    Thread.Sleep(10);
                    if (timer > WaitTimes)
                        break;
                }
                byte[] myBufferBytes = new byte[k];
                tcpClient.Receive(myBufferBytes, k, SocketFlags.None);
                return myBufferBytes;
            }
            catch (Exception)
            {
                return null;
            }
        }


        #endregion

        #region 读取多个通信组

        public List<SiemensGroup> ReadMultipleVars(List<SiemensGroup> SiemensGroup)
        {
            int cntBytes = SiemensGroup.Sum(dataItem => dataItem.Count);

            //特殊情况处理
            if (cntBytes > MaxPDUSize - 18)
            {
                if (SiemensGroup.Count != 1)
                {
                    return null;
                }
                else
                {
                    SiemensGroup[0].Value = ReadBytes(SiemensGroup[0].StoreType, SiemensGroup[0].DB, SiemensGroup[0].StartByteAdr, SiemensGroup[0].Count);
                    return SiemensGroup;
                }
            }

            // first create the header
            int packageSize = 19 + (SiemensGroup.Count * 12);
            ByteArray package = new ByteArray();
            package.Add(ReadHeaderPackage(SiemensGroup.Count).array);
            // package.Add(0x02);  // datenart
            foreach (var dataItem in SiemensGroup)
            {
                package.Add(CreateReadDataRequestPackage(dataItem.StoreType, dataItem.DB, dataItem.StartByteAdr, dataItem.Count));
            }

            var s7data = SendAndReceive(package.array);

            if (s7data == null) return null;

            if (ParseDataIntoDataItems(s7data, SiemensGroup))
            {
                return SiemensGroup;
            }
            else
            {
                return null;
            }
        }

        private bool ParseDataIntoDataItems(byte[] s7data, List<SiemensGroup> dataItems)
        {
            int offset = 21;
            foreach (var dataItem in dataItems)
            {
                // check for Return Code = Success
                if (s7data[offset] != 0xff)
                    return false;

                // to Data bytes
                offset += 4;

                int byteCnt = dataItem.Count;
                dataItem.Value = s7data.Skip(offset).Take(byteCnt).ToArray();

                // next Item
                offset += byteCnt;

                // Fill byte in response when bytecount is odd
                if (dataItem.Count % 2 != 0)
                    offset++;
            }

            return true;
        }

        private ByteArray CreateReadDataRequestPackage(StoreType dataType, int db, int startByteAdr, int count = 1)
        {
            //single data req = 12
            var package = new ByteArray();
            package.Add(new byte[] { 0x12, 0x0a, 0x10 });
            switch (dataType)
            {
                case StoreType.Timer:
                case StoreType.Counter:
                    package.Add((byte)dataType);
                    break;
                default:
                    package.Add(0x02);
                    break;
            }

            package.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)count));
            package.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)db));
            package.Add((byte)dataType);
            var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
            package.Add((byte)overflow);
            switch (dataType)
            {
                case StoreType.Timer:
                case StoreType.Counter:
                    package.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)startByteAdr));
                    break;
                default:
                    package.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(startByteAdr * 8)));
                    break;
            }

            return package;
        }

        private ByteArray ReadHeaderPackage(int amount = 1)
        {
            //header size = 19 bytes
            var package = new ByteArray();
            package.Add(new byte[] { 0x03, 0x00 });
            //complete package size
            package.Add(ByteArrayLib.GetByteArrayFromShort((short)(19 + (12 * amount))));
            package.Add(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 });
            //data part size
            package.Add(ByteArrayLib.GetByteArrayFromUShort((ushort)(2 + (amount * 12))));
            package.Add(new byte[] { 0x00, 0x00, 0x04 });
            //amount of requests
            package.Add((byte)amount);

            return package;
        }

        #endregion
    }

    /// <summary>
    /// 通信组
    /// </summary>
    public class SiemensGroup
    {
        public SiemensGroup()
        {
            this.StartByteAdr = 0;
            this.Count = 10;
        }

        public SiemensGroup(StoreType StoreType, int DB, int StartByteAdr, int Count)
        {
            this.StoreType = StoreType;
            this.DB = DB;
            this.StartByteAdr = StartByteAdr;
            this.Count = Count;
        }

        public StoreType StoreType { get; set; }

        public int DB { get; set; }

        public int StartByteAdr { get; set; }

        public int Count { get; set; }

        public byte[] Value { get; set; }

    }
}
